#include "cspinbox.h"

/**
 * CSpinBox - constructor, creates spin box from list of items
 * @param spinItems
 */
CSpinBox::CSpinBox(vector<CTextListItem> spinItems) {
  init();
  setSpinItems(spinItems);
}
//---------------------------------------------------------------------------

/**
 * CSpinBox - constructor, creates spin box from list of strings
 * @param spinItems
 */
CSpinBox::CSpinBox(vector<string> spinItems) {
  init();
  setSpinItems(spinItems);
}
//---------------------------------------------------------------------------

/**
 * CSpinBox - constructor, creates spin box from list of strings and ids
 * @param spinItems
 * @param spinItemIds
 */
CSpinBox::CSpinBox(vector<string> spinItems, vector<int> spinItemIds) {
  init();
  setSpinItems(spinItems, spinItemIds);
}
//---------------------------------------------------------------------------

/**
 * ~CSpinBox - destructor
 */
CSpinBox::~CSpinBox() {
  free();
}
//---------------------------------------------------------------------------

/**
 * init - initialization
 */
void CSpinBox::init() {

  // Initialization
  CDynamicGuiObject::init();

  // Set style without borders
  CGuiStyle noBorderStyle;
  noBorderStyle.setBorder(CBorder(0, CColor(0x000000)));
  setStyleNormal(noBorderStyle);
  setStylePrelight(noBorderStyle);
  setStyleFocus(noBorderStyle);
  setStyleDisabled(noBorderStyle);

  // Create nested components
  buttonUp = new CButton("<");
  buttonDown = new CButton(">");
  label = new CLabel();
}
//---------------------------------------------------------------------------

/**
 * onAdd - reaction on adding new item into list
 */
void CSpinBox::onAdd() {

  // Create layout
  unsigned short w((double) (width - style.getBorder().getSize() * 2) * 0.8);
  unsigned short h((height - style.getBorder().getSize() * 2) / 2);
  unsigned short deltaH = height - h * 2;
  vector<unsigned short> patternsX;
  vector<unsigned short> patternsY;
  patternsX.push_back(w);
  patternsX.push_back(1);
  patternsX.push_back(width - w - 1);
  patternsY.push_back(h);
  patternsY.push_back(deltaH);
  patternsY.push_back(h);
  layout = CLayout(patternsX, patternsY);

  // Set nested compoents font id
  // TODO: tohle delat v setFontId, nejlepe na nejvyssi urovni
  label->setFontId(fontId);
  buttonUp->setFontId(fontId);
  buttonDown->setFontId(fontId);

  // Place nested component into layout
  addItem(label, 0, 0, 1, 3);
  addItem(buttonUp, 2, 0);
  addItem(buttonDown, 2, 2);
}
//---------------------------------------------------------------------------

/**
 * isFocused - returns true if cursor is located above component
 * @param eventPtr
 * @return true/false cursor is/is not above component
 */
bool CSpinBox::isFocused(CMouseEvent* eventPtr) {

  // Temporary variables
  int l(x), t(y), r(x + width), b(y + height);
  int mx(eventPtr->getMouseX()), my(eventPtr->getMouseY());
  bool click(dynamic_cast<CMouseClickEvent*> (eventPtr));

  // Process click on component
  // TODO: mozno zefektivnit pres dispatch (udelat poradek)
  if (click && buttonUp->isFocused(eventPtr)) {
    moveSelectionUp();
  } else if (click && buttonDown->isFocused(eventPtr)) {
    moveSelectionDown();
  }

  // If mouse is above component, it is focused
  if (mx >= l && my >= t && mx <= r && my <= b) {
    return true;
  }

  return false;
}
//---------------------------------------------------------------------------

/**
 * getButtonUpPtr - returns pointer on upper control button
 * @return upper button pointer
 */
CButton* CSpinBox::getButtonUpPtr() const {
  return buttonUp;
}
//---------------------------------------------------------------------------

/**
 * getButtonDownPtr - returns pointer on lower control button
 * @return lower button pointer
 */
CButton* CSpinBox::getButtonDownPtr() const {
  return buttonDown;
}
//---------------------------------------------------------------------------

/**
 * getSelection - returns selected item
 * @return selected item
 */
vector<CTextListItem>::iterator CSpinBox::getSelection() const {
  return sel;
}
//---------------------------------------------------------------------------

/**
 * keyDown - key press on spin box
 * @param keyId
 * @param unicode
 */
void CSpinBox::keyDown(unsigned short keyId, unsigned short unicode) {

  // If arrow up was pressed, move selection up
  // Otherwise if arrow down was pressed, move selection down
  switch (keyId) {
    case CKeyIdentifications::KEY_UP :
      moveSelectionUp();
      break;
    case CKeyIdentifications::KEY_DOWN :
      moveSelectionDown();
      break;
  }
}
//---------------------------------------------------------------------------

/**
 * moveSelectionUp - move slection up
 */
void CSpinBox::moveSelectionUp() {
  if (sel != spinItems.begin()) {
    label->setText((*(--sel)).getValue());
  }
}
//---------------------------------------------------------------------------

/**
 * moveSelectionDown - move selection down
 */
void CSpinBox::moveSelectionDown() {
  if (sel + 1 != spinItems.end()) {
    label->setText((*(++sel)).getValue());
  }
}
//---------------------------------------------------------------------------

/**
 * free - free nested components
 */
void CSpinBox::free() {
  CGuiObject::free();
  if (buttonUp) {
    delete buttonUp;
    buttonUp = NULL;
  }
  if (buttonDown) {
    delete buttonDown;
    buttonDown = NULL;
  }
  if (label) {
    delete label;
    label = NULL;
  }
}
//---------------------------------------------------------------------------

/**
 * setSpinItems - replace list of items with new one
 * @param spinItems
 */
void CSpinBox::setSpinItems(vector<CTextListItem> spinItems) {
  this->spinItems = spinItems;
  sel = this->spinItems.begin();
  label->setText((*sel).getValue());
}
//---------------------------------------------------------------------------

/**
 * setSpinItems - replace list of items with new one
 * @param spinItems
 */
void CSpinBox::setSpinItems(vector<string> spinItems) {
  this->spinItems.clear();
  vector<string>::iterator i = spinItems.begin();
  for (int pos = 0; i < spinItems.end(); i++, pos++) {
    this->spinItems.push_back(CTextListItem(*i, pos));
  }
  sel = this->spinItems.begin();
  label->setText((*sel).getValue());
}
//---------------------------------------------------------------------------

/**
 * setSpinItems - replace list of items with new one
 * @param spinItems
 * @param spinItemIds
 */
void CSpinBox::setSpinItems(vector<string> spinItems, vector<int> spinItemIds) {

  // Check if both lists are of same size
  if (spinItems.size() != spinItemIds.size()) {
    throw CInvalidSizeException(getClassName(), "setSpinItems");
  }

  // Replace with new items
  this->spinItems.clear();
  vector<string>::iterator i = spinItems.begin();
  vector<int>::iterator j = spinItemIds.begin();
  for (; i < spinItems.end(); i++, j++) {
    this->spinItems.push_back(CTextListItem(*i, *j));
  }
  sel = this->spinItems.begin();
  label->setText((*sel).getValue());
}
//---------------------------------------------------------------------------
